#!/bin/sh
# PCP QA Test No. 917
# SELinux Testing
#
# Copyright (c) 2017-2018 Red Hat Inc.  All Rights Reserved.
#

seq=`basename $0`
echo "QA output created by $seq"

# get standard environment, filters and checks
. ./common.product
. ./common.filter
. ./common.check

policy_name="pcpupstream"
policy_file="$PCP_VAR_DIR/selinux/$policy_name.pp"
which sedismod >/dev/null 2>&1 || _notrun "sedismod tool not installed (module disassembly)"
which semodule >/dev/null 2>&1 || _notrun "semodule tool not installed"
which seinfo >/dev/null 2>&1 || _notrun "seinfo tool not installed"
( seinfo -t 2>&1 | grep 'Default policy search failed: No such file or directory' >/dev/null ) && _notrun "seinfo version bad: can't load default policy"
[ -f "$policy_file" ] || _notrun "upstream policy package not installed"
$sudo semodule -l 2>&1 | grep -q $policy_name || _notrun "upstream policy package not loaded"
[ -f $PCP_INC_DIR/builddefs ] || _notrun "No $PCP_INC_DIR/builddefs"

seinfo --common >/dev/null 2>&1
if [ $? -eq 0 ]
then
    common_flag="--common"
else
    common_flag=""
fi

_filter_semodule()
{
    awk '{ print $1 }'
}

_filter_sedismod()
{
    awk '
/^--- begin avrule block ---/	{ body=1 }
	{ if (body) print $0 }' | \
    sed \
	-e '/^--- begin avrule block ---/d' \
	-e '/^decl [0-9[0-9]*/d' \
	-e '/pcp_pmwebd_t/d' \
	-e '/pcp_pmmgr_t/d' \
	-e '/^Command/d' \
    #end
}

_filter_outfile()
{
    sed -f $tmp.sed
}

status=1	# failure is the default!
$sudo rm -rf $tmp $tmp.* $seq.full
trap "cd $here; $sudo rm -rf $tmp $tmp.*; exit \$status" 0 1 2 3 15

# use logic from configure.ac to build list of optional types that are
# not present on this system and need to be culled from $seq.out.in
#
seinfo -t >$tmp.types
echo '/^#/d' >$tmp.sed
echo '/^!/s// /' >>$tmp.sed
for type in container_runtime_t nsfs_t docker_var_lib_t unreserved_port_t \
	    tracefs_t unconfined_service_t numad_t rpm_var_lib_t \
	    virt_var_run_t proc_security_t sbd_exec_t
do
    if grep "^[ 	][ 	]*$type\$" $tmp.types >/dev/null
    then
	:
    else
	echo "/^  *$type\$/d" >>$tmp.sed
	# and some missing types => associated rules need to be culled or
	# edited
	#
	case "$type"
	in
	    nsfs_t)
		echo '/allow \[pcp_pmcd_t] \[nsfs_t]/d' >>$tmp.sed
		;;
	    unreserved_port_t)
		echo '/allow \[pcp_pmcd_t] \[unreserved_port_t]/d' >>$tmp.sed
		echo '/allow \[pcp_pmlogger_t] \[unreserved_port_t]/d' >>$tmp.sed
		;;
	    tracefs_t)
		echo '/allow \[pcp_pmcd_t] \[tracefs_t]/d' >>$tmp.sed
		;;
	    unconfined_service_t)
		echo '/allow \[pcp_pmcd_t] \[unconfined_service_t]/d' >>$tmp.sed
		echo '/allow \[pcp_pmlogger_t] \[unconfined_service_t]/d' >>$tmp.sed
		echo '/allow \[pcp_pmie_t] \[unconfined_service_t]/d' >>$tmp.sed
		;;
	    numad_t)
		echo '/allow \[pcp_pmcd_t] \[numad_t]/d' >>$tmp.sed
		;;
	    rpm_var_lib_t)
		echo '/allow \[pcp_pmcd_t] \[rpm_var_lib_t]/d' >>$tmp.sed
		;;
	    virt_var_run_t)
		echo '/allow \[pcp_pmcd_t] \[virt_var_run_t]/d' >>$tmp.sed
		;;
	    proc_security_t)
		echo '/allow \[pcp_pmcd_t] \[proc_security_t]/d' >>$tmp.sed
		;;
	    sbd_exec_t)
		echo '/allow \[pcp_pmcd_t] \[sbd_exec_t]/d' >>$tmp.sed
		;;
	esac
    fi
done

# now the class ones ... also using logic from configure.ac
#
if seinfo -x --class=cap_userns $common_flag 2>&1 \
   | grep '^[ 	][ 	]*sys_ptrace$' >/dev/null
then
    :
else
    echo '/allow \[pcp_pmie_t] .*\[cap_userns]/d' >>$tmp.sed
fi

if seinfo -x --class=file $common_flag 2>&1 \
   | grep '^[ 	][ 	]*map$' >/dev/null
then
    :
elif seinfo -x --common file 2>&1 \
   | grep '^[ 	][ 	]*map$' >/dev/null
then
    :
else
    # if no map, need to cull these one as map is the only permission
    #
    echo '/allow \[pcp_pmcd_t] \[ldconfig_exec_t] : \[file].* map/d' >>$tmp.sed
    echo '/allow \[pcp_pmcd_t] \[rpm_var_lib_t] : \[file].* map/d' >>$tmp.sed
    echo '/allow \[pcp_pmcd_t] \[default_t] : \[file].* map/d' >>$tmp.sed
    # strip "map" from permissions for others
    #
    echo '/\[pcp_pmie_exec_t] .*\[file]/s/ map / /' >>$tmp.sed
    echo '/\[pcp_pmcd_t] .*\[file]/s/ map / /' >>$tmp.sed
    echo '/\[pcp_pmie_t] .*\[hostname_exec_t]/s/ map / /' >>$tmp.sed
    echo '/\[pcp_pmcd_t] \[fsadm_exec_t]/s/ map / /' >>$tmp.sed
    echo '/\[pcp_pmcd_t] \[default_t]/s/ map / /' >>$tmp.sed
    echo '/\[pcp_pmcd_t] \[pcp_pmie_exec_t]/s/ map / /' >>$tmp.sed
    echo '/\[pcp_pmcd_t] \[pcp_tmp_t]/s/ map / /' >>$tmp.sed
    echo '/\[pcp_pmcd_t] \[ping_exec_t]/s/ map / /' >>$tmp.sed
fi

if seinfo -x --class=bpf $common_flag 2>&1 \
   | egrep '^[ 	][ 	]*(class |)bpf$' >/dev/null
then
    :
else
    echo '/allow \[pcp_pmcd_t] .*\[bpf]/d' >>$tmp.sed
fi

if seinfo -x --class=capability2 $common_flag 2>&1 \
   | grep '^[ 	][ 	]*syslog$' >/dev/null
then
    :
else
    echo '/allow \[pcp_pmcd_t\] .*\[capability2\]/d' >>$tmp.sed
fi

# rawip_socket is indeed nested and only present if the icmp_socket class
# IS NOT defined AND the rawip_socket class IS defined
# (see the selinux GNUlocaldefs file) ... so in the other cases we need
# to cull the rawip_socket line from $seq.out.in
#
if seinfo -x --class=icmp_socket $common_flag 2>&1 \
   | egrep '^[ 	][ 	]*(class |)icmp_socket$' >/dev/null
then
    echo '/allow \[pcp_pmcd_t\] .*\[rawip_socket\]/d' >>$tmp.sed
else
    echo '/allow \[pcp_pmcd_t\] .*\[icmp_socket\]/d' >>$tmp.sed
    if seinfo -x --class=rawip_socket $common_flag 2>&1 \
       | egrep '^[ 	][ 	]*(class |)rawip_socket$' >/dev/null
    then
	:
    else
	echo '/allow \[pcp_pmcd_t\] .*\[rawip_socket\]/d' >>$tmp.sed
    fi
fi

if seinfo -x --class=netlink_generic_socket $common_flag 2>&1 \
   | egrep '^[ 	][ 	]*(class |)netlink_generic_socket$' >/dev/null
then
    :
else
    echo '/allow \[pcp_pmcd_t\] .*\[netlink_generic_socket\]/d' >>$tmp.sed
fi

if seinfo -a 2>&1 \
   | grep '^[ 	][ 	]*non_auth_file_type$' >/dev/null
then
    echo '/allow \[pcp_domain] \[non_security_file_type]/d' >>$tmp.sed
else
    echo '/allow \[pcp_domain] \[non_auth_file_type]/d' >>$tmp.sed
fi

if grep 'PCP_SELINUX_FILES_MMAP_ALL_FILES[ 	]*=[ 	]*true' $PCP_INC_DIR/builddefs >/dev/null 2>&1
then
    :
else
    echo '/allow \[pcp_domain] \[file_type] : \[file].* map/d' >>$tmp.sed
fi

cat $tmp.sed >>$seq.full

cat $seq.out.in | _filter_outfile >$seq.out

echo "full policy modules list on the system"
$sudo semodule -l >>$seq.full
echo "Checking that pcpupstream policy module has been properly installed"
awk '{ print $1 }' $seq.full | grep "pcpupstream$"  | _filter_semodule
# real QA test starts here
echo "Checking policies."
printf '1\nq\n' | sedismod $policy_file | tee -a $seq.full | _filter_sedismod

# success, all done
status=0
exit
